/*
 */
package compilador;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 *
 * @author Melinton
 */
public class Loader
{
    
    //--------------------------------------------------------------------------
    // ATRIBUTOS
    //--------------------------------------------------------------------------
    public HashMap characters;
    public HashMap keywords;
    public HashMap tokens;
    public HashMap productions;
    public HashMap regCharacters;
    public HashMap regKeywords;
    public HashMap regTokens;
    public HashMap regProductions;
    public HashMap metodos;
    public HashMap equivalencias;
    
    //Reader related
    private CharBuffer input;
    private static final int LOOKAHEAD_DEPTH = 11;
    
    //--------------------------------------------------------------------------
    // CONSTRUCTOR
    //--------------------------------------------------------------------------
    public Loader(Reader f)
    {
        input = new CharBuffer(f, LOOKAHEAD_DEPTH);
        characters = new HashMap();
        keywords = new HashMap();
        tokens = new HashMap();
        productions = new HashMap();
        regCharacters = new HashMap();
        regKeywords = new HashMap();
        regTokens = new HashMap();
        regProductions = new HashMap();
        metodos = new HashMap();
        equivalencias = new HashMap();
        
    }
    
    //--------------------------------------------------------------------------
    // METODOS
    //--------------------------------------------------------------------------              
    private int LA(int i) { return input.LA(i); }
    private void consume() { input.consume(); }
    private void match(int c) { consume(); }
    
    public void inputFile() 
    {
        while ( LA(1) != -1 ) 
        {
            //Match de comentarios
            if ( LA(1)=='(' && LA(2)=='.') 
            { 
                comment();
            }
            
            // Match the word COMPILER
            else if ( LA(1) == 'C' && LA(2) == 'O' && LA(3) == 'M' && LA(4) == 'P' && LA(5) == 'I' && LA(6) == 'L' && LA(7) == 'E' && LA(8) == 'R') 
            {
                kwCompiler();
                match(' ');
                name();                
            }
            
            // Match CHARACTERS
            else if(LA(1) == 'C' && LA(2) == 'H' && LA(3) == 'A' && LA(4) == 'R' && LA(5) == 'A' && LA(6) == 'C' && LA(7) == 'T' && LA(8) == 'E' && LA(9) == 'R' && LA(10) == 'S')
            {
                kwCharacters();
                while(LA(1)=='\n')
                    match('\n');
                while(LA(1)!='\n')
                {
                    assignmentCh();
                }                
            }
            
            // Match KEYWORDS
            else if(LA(1) == 'K' && LA(2) == 'E' && LA(3) == 'Y' && LA(4) == 'W' && LA(5) == 'O' && LA(6) == 'R' && LA(7) == 'D' && LA(8) == 'S')
            {
                kwKeywords();
                while(LA(1)=='\n')
                    match('\n');
                while(LA(1)!='\n')
                {
                    assignmentKw();
                }                
            }
            
            // Match TOKENS
            else if(LA(1) == 'T' && LA(2) == 'O' && LA(3) == 'K' && LA(4) == 'E' && LA(5) == 'N' && LA(6) == 'S')
            {
                kwTokens();
                while(LA(1)=='\n')
                    match('\n');
                while(LA(1)!='\n')
                {
                    assignmentTk();
                }                                
            }
            
            // Match PRODUCTIONS
            else if(LA(1) == 'P' && LA(2) == 'R' && LA(3) == 'O' && LA(4) == 'D' && LA(5) == 'U' && LA(6) == 'C' && LA(7) == 'T' && LA(8) == 'I' && LA(9) == 'O' && LA(10) == 'N' && LA(11) == 'S')
            {
                kwProductions();
                match('\n');
                match('\n');
                //TODO Productions assignments?
                StringBuffer s = new StringBuffer();
                
                while(LA(1)!='\n' || LA(2) != '\n')
                {
                    ArrayList regex = new ArrayList();
                    if(LA(1) == '\n')
                        match('\n');
                    while(LA(1) != '=')
                    {
                        if(LA(1)=='<')
                            s.append('(');
                        else if(LA(1)=='>')
                            s.append(')');
                        else
                            s.append((char)LA(1));
                        consume();
                    }        
                    match('=');
                    String identifier = s.toString();
                    s.delete(0,s.length());
                        
                    while(LA(1) != '.')
                    {
                        if(LA(1) == '{')
                        {
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), '(');
                            match('{');
                        }
                        else if(Character.isLetter((char) LA(1)))
                        {
                            while(Character.isLetter((char) LA(1)) || Character.isDigit((char)LA(1))|| LA(1) == '<' || LA(1) == '>')
                            {
                                if(LA(1)=='<')
                                    s.append('(');
                                else if(LA(1)=='>')
                                    s.append(')');
                                else
                                    s.append((char)LA(1));
                                consume();
                            }
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), s.toString());
                            s.delete(0, s.length());
                        }
                        else if(LA(1) == '(' && LA(2) != '.')
                        {
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), '(');
                            match('(');
                        } 
                        else if(LA(1) == '"')
                        {
                            match('"');
                            while(LA(1) != '"')
                            {
                                s.append((char)LA(1));
                                consume();
                            }
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), s.toString());
                            s.delete(0, s.length());
                            match('"');
                        }
                        else if(LA(1) == ')')
                        {
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), ')');
                            match(')');
                        }
                        else if(LA(1) == '}')
                        {
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), ')');
                            match(')');
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), '*');                            
                        }
                        else if(LA(1) == '(' && LA(2) == '.')
                        {
                            match('(');
                            match('.');
                            while(LA(1) != '.' || LA(2) != ')')
                            {
                                s.append((char)LA(1));
                                consume();
                            }
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), s.toString());
                            s.delete(0, s.length());
                            match('.');
                            match(')');
                        }
                        else if(LA(1)=='|')
                        {
                            match('|');
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), '|');
                        }
                        else if(LA(1) == '[')
                        {
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), '[');
                            match('[');
                        }
                        else if(LA(1) == ']')
                        {
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), ']');
                            match(']');
                            regex.add(equivalencias.size());
                            equivalencias.put(equivalencias.size(), '?');     
                        }
                        else
                        {
                            consume();
                        }
                    }
                    match('.');
                    metodos.put(identifier, regex);
                }
            }
            
            // Match END
            else if(LA(1) == 'E' && LA(2) == 'N' && LA(3) == 'D')
            {
                kwEnd();
                match(' ');
                name();
            }
            
            else 
            {
                //assignment();
                match('\n');
            }
        }
    }
    
    public void assignmentCh() 
    {        
        String identifier = identifier();
        if(LA(1)==' ')
            match(' ');
        match('=');
        if(LA(1)==' ')
            match(' ');
        String value = value();
        characters.put(identifier, value);   
        match('\n');
    }    
    
    public void assignmentKw() 
    {
        String id = identifier();
        if(LA(1)==' ')
            match(' '); 
        match('=');
        if(LA(1)==' ')
            match(' ');
        String val = value();
        keywords.put(id, val);
        match('\n');
    }  
    
    public void assignmentTk() 
    {
        String id = identifier();
        if(LA(1)==' ')
            match(' ');
        match('=');
        if(LA(1)==' ')
            match(' ');
        String val = valueAndExcept();
        tokens.put(id, val);
        match('\n');
    }    
    
    public String identifier() /* TODO ACCORDING TO THE PROJECT */
    {
        StringBuffer s = new StringBuffer();
        while ( Character.isLetter((char)LA(1)) || Character.isDigit((char) LA(1)))
        {
            s.append((char)LA(1));
            consume();
        }        
        return s.toString(); 
    }
    
    public String identifierWithPar() /* TODO ACCORDING TO THE PROJECT */
    {
        StringBuffer s = new StringBuffer();
        while ( Character.isLetter((char)LA(1)) || Character.isDigit((char) LA(1)) || LA(1)=='(' || LA(1)==')')
        {
            s.append((char)LA(1));
            consume();
        }        
        return s.toString(); 
    }
    
    public String value() 
    {
        StringBuffer s = new StringBuffer();
        ArrayList<String> identifierIncluded = new ArrayList();
        if(LA(1)=='"')
        {
            match('"');
            while ( Character.isLetter((char)LA(1)) || Character.isDigit((char) LA(1)) ) 
            {
                s.append((char)LA(1));
                consume();
            }
            match('"');
            
            if(LA(1)=='\n')
                match('\n');
            else
            {
                while(LA(1)=='+')
                {
                    match('+');
                    identifierIncluded.add(identifier());
                }                
            }
        }
        
        else
        {
            identifierIncluded.add(identifier());
            while(LA(1)=='+' && LA(2)!='"')
            {
                identifierIncluded.add(identifier());
            }
            match('+');           
            match('"');
            while ( Character.isLetter((char)LA(1)) || Character.isDigit((char) LA(1)) ) 
            {
                s.append((char)LA(1));
                consume();
            }
            match('"');                        
        }
        String cadenaIdentifiersIncluded = "";
        for(int i = 0; i < identifierIncluded.size(); i++)
        {
            cadenaIdentifiersIncluded += (String) characters.get(identifierIncluded.get(i));
        }
        String cadenaCompleta = s.toString() + cadenaIdentifiersIncluded;
        match('.');
        return cadenaCompleta;
    }
    
    public String valueAndExcept()
    {
        StringBuffer s = new StringBuffer();
        String pivote = "";
        while(LA(1)!='\n')
        {
            if(LA(1)=='"')
            {
                pivote+='"';
                match('"');
                pivote += identifierWithPar();
                pivote+='"';
                match('"');
            }
            else if(LA(1)=='{')
            {
                pivote+=(char) LA(1);
                match('{');
                pivote+=identifier();
                while(((char) LA(1)) == '|')
                {
                    pivote+= (char) LA(1);
                    match('|');
                    pivote+= identifier();
                }
                pivote+=(char) LA(1);
                match('}');                
            }
            else if (LA(1)==' ')
            {
                consume();
                if(LA(1)=='E' && LA(2)=='X' && LA(3)=='C' && LA(4) == 'E' && LA(5)=='P' && LA(6) == 'T')
                {
                    kwExcept();
                    match(' ');
                    pivote+=" -";
                    pivote+=identifier();
                    match('.');
                }
            }
            else if(Character.isLetter((char) LA(1)) || Character.isDigit((char) LA(1)))
            {
                pivote+=identifier();
            }
            else
                match('\n');
        }
        return pivote;
        
    }

    public String comment() 
    {
        match('(');
        match('.');        
        StringBuffer s = new StringBuffer();
        while ( LA(1)!='.' && LA(2)!=')' ) 
        {
            s.append((char)LA(1));
            consume();
        }
        match('.');
        match(')');
        return s.toString();
    }
    
    public String name()            
    {
        StringBuffer s = new StringBuffer();
        while(Character.isLetter((char) LA(1)) || Character.isDigit((char) LA(1)))
        {
            s.append((char)LA(1));
            consume();
        }
        
        return s.toString();
    }
    
    public void transformarRegex()
    {
        Iterator iterator = characters.keySet().iterator();
        String pivotKey = "";
        String pivotValue = "";
        String pivotValueReg;
        
        /** Iterator para convertir los CHARACTERS */
        while(iterator.hasNext())
        {
            pivotKey = (String) iterator.next();
            pivotValue = (String) characters.get(pivotKey);
            pivotValueReg = "(";
            StringBuffer s = new StringBuffer();
            for(int i = 0; i < pivotValue.length(); i++)
            {
                if(i==0)
                    s.append(pivotValue.charAt(i));
                else
                {
                    s.append('|');
                    s.append(pivotValue.charAt(i));
                }
            }
            pivotValueReg += s.toString() + ")";
            regCharacters.put(pivotKey, pivotValueReg);
        }
        
        /** Convertir los KEYWORDS */
        //Las keywords se quedan igual, son concatenaciones
        iterator = keywords.keySet().iterator();
        while(iterator.hasNext())
        {
            pivotKey = (String) iterator.next();
            pivotValue = (String) keywords.get(pivotKey);
            regKeywords.put(pivotKey,pivotValue);
        }
        
        /** Convertir los TOKENS */
        iterator = tokens.keySet().iterator();
        while(iterator.hasNext())
        {
            pivotKey = (String) iterator.next();
            pivotValue = (String) tokens.get(pivotKey);
            pivotValueReg = "";
            StringBuffer s = new StringBuffer();
            boolean exception = false;
            int i = 0;
            while(i<pivotValue.length() && !exception)
            {
                if(Character.isLetter(pivotValue.charAt(i)) || Character.isDigit(pivotValue.charAt(i)) || pivotValue.charAt(i)=='{')
                {
                    while(Character.isLetter(pivotValue.charAt(i)) || Character.isDigit(pivotValue.charAt(i)))
                    {
                        s.append(pivotValue.charAt(i));
                        i++;
                    }
                    pivotValueReg += (String) regCharacters.get(s.toString());
                    if(pivotValue.charAt(i)=='{')
                    {
                        i++;
                        //pivotValueReg += '(';
                        StringBuffer s1 = new StringBuffer();
                        while(Character.isLetter(pivotValue.charAt(i)) || Character.isDigit(pivotValue.charAt(i)))                                                    
                        {
                            s1.append(pivotValue.charAt(i));
                            i++;
                        }
                        pivotValueReg += (String) regCharacters.get(s1.toString());
                        if(pivotValue.charAt(i)=='|')
                        {
                            StringBuffer s2 = new StringBuffer();
                            i++;
                            while(Character.isLetter(pivotValue.charAt(i)) || Character.isDigit(pivotValue.charAt(i)))                                                    
                            {
                                s2.append(pivotValue.charAt(i));
                                i++;
                            }
                            pivotValueReg += (String) regCharacters.get(s2.toString());
                        }
                        
                        //pivotValueReg+=')';
                        pivotValueReg+='*';                        
                    } 
                }
                if(pivotValue.charAt(i)=='-')
                    exception = true;
                i++;
            }
            if(pivotKey != null)
                regTokens.put(pivotKey, pivotValueReg);
        }
        
    }
    
    public void reemplazarSimbolosEspeciales()
    {
        Iterator it = characters.keySet().iterator();
    }
            
    
    public String kwCompiler() 
    {
        match('c');
        match('o');
        match('m');
        match('p');
        match('i');
        match('l');
        match('e');
        match('r');
        return "compiler";
    }
    
    public String kwKeywords()
    {
        match('k');
        match('e');
        match('y');
        match('w');
        match('o');
        match('r');
        match('d');
        match('s');        
        return "keywords";
    }
    
    public String kwTokens()
    {
        match('t');
        match('o');
        match('k');
        match('e');
        match('n');
        match('s');        
        return "tokens";
    }
    
    public String kwProductions()
    {
        match('p');
        match('r');
        match('o');
        match('d');
        match('u');
        match('c');
        match('t');
        match('i');
        match('o');
        match('n');
        match('s');           
        return "productions";
    }
    
    public String kwExcept()
    {
        match('e');
        match('x');
        match('c');
        match('e');
        match('p');
        match('t');        
        return "except";
    }
    
    public String kwCharacters()
    {
        match('c');
        match('h');
        match('a');
        match('r');
        match('a');
        match('c');
        match('t');
        match('e');
        match('r');
        match('s');        
        return "characters";
    }
    
    public String kwEnd()
    {
        match('e');
        match('n');
        match('d');
        return "end";
    }
   
}     
    